﻿using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using BlogsAPI.Interfaces;
using VkontakteSharp;
using VkontakteSharp.Endpoints.Wall;
using VkontakteSharp.Enums;
using VkontakteSharp.Model;

namespace BlogsAPI.Sites
{
    using System.Web;
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Net;
    using System.Text;
    using System.Text.RegularExpressions;
    using Abstract;
    using Group = VkontakteSharp.Model.Group;

    /// <summary>
    /// Русскоязычный клон <see cref="Facebook"/>
    /// </summary>
    [BlogServiceAPI(Description = "Vkontakte", Link = "http://vk.com")]
    public class Vkontakte : BlogServer, ILike
    {
        /// <summary>
        /// Ключ приложения из настроек
        /// </summary>
        public static string ServiceKey = "2facdad62facdad62facdad6dc2f97202622fac2facdad6775a8987a25c1503712ed0b1";

        private static readonly Regex Validation = new Regex(@"^https?://(?:[-_\w]+\.)?(?:vkontakte\.ru|vk\.com)", Regexoptions);
        private static readonly Regex ValidationFull = new Regex(@"^https?://(?:[-_\w]+\.)?(?:vkontakte\.ru|vk\.com)/([^0-9]*?)(-?[0-9]+)(?:[^0-9]*?|$)", Regexoptions);
        
        /// <summary>
        /// Уникальный ключ для простых запросов
        /// </summary>
        public static VkontakteConfig APIKey
            = new VkontakteConfig("https://api.vk.com/method", "https://oauth.vk.com/authorize", "2668173", "", "");

        static readonly Regex rWallID = new Regex(@"(?:^|\D)(-?\d+_\d+)(?:$|\D)", Regexoptions);

        /// <summary>
        /// Ключ доступа для запросов
        /// </summary>
        public string AccessToken;

        #region interface: BlogServer

        [DebuggerStepThrough]
        public Vkontakte()
        {
            Encoding = Encoding.UTF8;
        }

        /// <summary>
        /// http://www.t30p.ru/Vktop.aspx/people
        /// </summary>
        public override bool HasRating
        {
            get { return true; }
        }

        /// <summary>
        /// проверка, является ли передаваемый урл чатью этого сервиса
        /// </summary>
        public override Boolean ValidateLink(String sLink)
        {
            return Validation.Match(sLink).Success;
        }

        /// <summary>
        /// проверка, является ли передаваемый урл чатью этого сервиса
        /// </summary>
        /// <param name="sLink">ссылка для проверки</param>
        /// <param name="userName">Имя пользователя на блогохостинге</param>
        /// <param name="bType">Тип блога - пользователь или сообщество</param>
        public override Boolean ValidateLink(String sLink, out String userName, out BlogType bType)
        {
            bType = BlogType.Unknown;
            Match m = ValidationFull.Match(sLink);
            if (m.Success)
            {
                //http://vkontakte.ru/id991058
                //http://vk.com/club37282761
                //http://vk.com/public37282761
                //http://vk.com/note-44874673_11663774
                //http://vk.com/wall-52155785_47
                bType = m.Groups[1].Value.ToLower()=="club"
                    || m.Groups[1].Value.ToLower() == "public"
                    || m.Groups[2].Value.StartsWith("-") ?BlogType.Community : BlogType.User;
                userName = m.Groups[2].Value;
            }
            else
            {
                userName = String.Empty;
            }
            return m.Success;
        }

        /// <summary>
        /// Получение ссылки на блог блогера
        /// </summary>
        /// <param name="userName">ник блогера</param>
        /// <param name="btype">Тип блога - пользователь или сообщество</param>
        public override String GetBlogLink(string userName, BlogType btype)
        {
            return String.Format("http://vk.com/{0}{1}",
                userName.StartsWith("-") | btype == BlogType.Community? "club" : "id", 
                userName.TrimStart('-'));
        }

        /// <summary>
        /// Получение несколько постов по ссылкам, групповая операция
        /// </summary>
        /// <param name="oLinks">массив ссылок и титлов</param>
        public List<BlogPost> GetManyPost(string[] oLinks)
        {
            WallRecord2<WallRecord> ret;
            return GetManyPost(oLinks, out ret);
        }

        /// <summary>
        /// Получение несколько постов по ссылкам, групповая операция
        /// </summary>
        /// <param name="oLinks">массив ссылок и титлов</param>
        /// <param name="rawdata">Сырые данные</param>
        public List<BlogPost> GetManyPost(string[] oLinks, out WallRecord2<WallRecord> rawdata)
        {
            var bpRet = new List<BlogPost>();
            rawdata = null;
            try
            {
                string[] IDs = (from link in oLinks where !String.IsNullOrEmpty(link) && link.Contains("/wall") 
                                    select rWallID.Match(link).Groups[1].Value).ToArray();
                if (IDs.Length > 0)
                {
                    //var client = new Unauthenticated(APIKey);
                    Authenticated client = new Authenticated(Vkontakte.APIKey, Vkontakte.ServiceKey);
                    ResponseClass<WallRecord2<WallRecord>> data = client.GetById(IDs);
                    if (data != null)//date === null? wtf??
                    {
                        if (data.Error == null)
                        {
                            rawdata = data.Data;
                            foreach (WallRecord record in data.Data.Records)
                            {
                                string username = null;
                                var user = data.Data.Users.FirstOrDefault(i => i.Id == record.ToId);
                                if (user != null)
                                {
                                    username = user.FirstName + " " + user.LastName;
                                }
                                else
                                {
                                    var grp = data.Data.Groups.FirstOrDefault(i => i.Id == -record.ToId);
                                    if (grp != null)
                                    {
                                        username = grp.ScreenName;
                                    }
                                }
                                bpRet.Add(ToBlogPost(record, username));
                            }
                        }
                        else
                        {
                            throw new Exception(data.Error.Code + ": " + data.Error.Message);
                        }
                    }
                }
            }catch(Exception e1)
            {
                Debug.WriteLine(e1);
            }
            return bpRet;
        }

        /// <summary>
        /// Преобразование данных из API во внутренний формат поста
        /// </summary>
        /// <param name="record"></param>
        /// <param name="userName">Настоящее имя пользователя</param>
        /// <returns></returns>
        public static BlogPost ToBlogPost(BasicRecord record, string userName)
        {
            BlogPost bp = BlogPost.SimpleLink(record.Link);
            bp.Body = record.Text;
            //ToId == -40137578 == club
            if (record is WallRecord)
            {
                bp.SpecialParam = (record as WallRecord).ToId.ToString();
            }
            else if (record is Newsfeed)
            {
                bp.SpecialParam = (record as Newsfeed).OwnerId.ToString();
            }
            //
            if (!String.IsNullOrEmpty(userName))
            {
                bp.SpecialParam = userName;
            }
            //
            bp.Comments = record.Comments.Count;
            bp.Views = record.Likes.Count + record.Comments.Count + record.Reposts.Count;
            bp.Links = record.Reposts.Count;
            bp.Published = record.Date;
            bp.Existance = true;
            bool hasMediaThumbnail = false;
            foreach (Attachment attachment in record.Attachments)
            {
                if (attachment is Link)
                {
                    var link = attachment as Link;
                    bp.Body += String.Format(" <a href='{0}'>{1}</a>", link.Url, 
                        String.IsNullOrEmpty(link.Description)?link.Url:link.Description);
                    if (!String.IsNullOrEmpty(link.ImageSrc))
                    {
                        bp.Attributes.Add(new PostAttribute(PostParams.MediaThumbnail, link.ImageSrc));
                    }
                }
                else if (attachment is Photo)
                {
                    var photo = attachment as Photo;
                    if (!String.IsNullOrEmpty(photo.SrcSmall) && !hasMediaThumbnail)
                    {
                        hasMediaThumbnail = true;
                        bp.Attributes.Add(new PostAttribute(PostParams.MediaThumbnail, photo.Src));
                    }
                }
                else if (attachment is Video)
                {
                    var video = attachment as Video;
                    if (!String.IsNullOrEmpty(video.Link))
                    {
                        bp.Body += String.Format(" <a href='{0}'>{1}</a>", video.Link.Replace("&", "&amp;"), video.Title);
                    }
                    else if (!String.IsNullOrEmpty(video.Title))
                    {
                        bp.Body += " " + video.Title;
                    }
                    if (!String.IsNullOrEmpty(video.Image) && !hasMediaThumbnail)
                    {
                        hasMediaThumbnail = true;
                        bp.Attributes.Add(new PostAttribute(PostParams.MediaThumbnail, video.Image));
                    }
                }
                else if (attachment is Document)
                {
                    var doc = attachment as Document;
                    if (!String.IsNullOrEmpty(doc.Url))
                    {
                        //гифки
                        if (!hasMediaThumbnail && String.Compare(doc.Ext, "gif", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            bp.Body += " " + doc.Title;
                            bp.Attributes.Add(new PostAttribute(PostParams.Tags, "s="+doc.Size));//размер гифки
                            bp.Attributes.Add(new PostAttribute(PostParams.MediaThumbnail, doc.Url));
                            break;//берем только первую гифку
                        }
                        else
                        {
                            bp.Body += String.Format(" <a href='{0}'>{1}</a>", doc.Url.Replace("&", "&amp;"), doc.Title);
                        }
                    }
                    else if (!String.IsNullOrEmpty(doc.Title))
                    {
                        bp.Body += " " + doc.Title;
                    }
                }
                else if (attachment is Audio)
                {
                    var audio = attachment as Audio;
                    if (!String.IsNullOrEmpty(audio.Url))
                    {
                        bp.Body += String.Format(" <a href='{0}'>{1}</a>", audio.Url.Replace("&", "&amp;"), audio.Title);
                    }
                    else if (!String.IsNullOrEmpty(audio.Title))
                    {
                        bp.Body += " " + audio.Title;
                    }
                    if (!String.IsNullOrEmpty(audio.Artist))
                    {
                        bp.Body += " " + audio.Artist;
                    }
                    if (!String.IsNullOrEmpty(audio.Performer))
                    {
                        bp.Body += " " + audio.Performer;
                    }
                }
                else if (attachment is Poll)
                {
                    var poll = attachment as Poll;
                    if (!String.IsNullOrEmpty(poll.Question))
                    {
                        bp.Body += " " + poll.Question;
                    }
                }
            }
            if (record.Geo != null && record.Geo.Coordinates != null)
            {
                bp.Attributes.Add(new PostAttribute(PostParams.Location,
                                                    String.Format(new CultureInfo("en-US"), "{0} {1}", 
                                                                  record.Geo.Coordinates.Latitude,
                                                                  record.Geo.Coordinates.Longitude)));
                if (record.Geo.Place != null && !String.IsNullOrEmpty(record.Geo.Place.Title))
                {
                    bp.Body += " " + record.Geo.Place.Title;//описание мероприятия
                }
                if (record.Geo.Place != null && !String.IsNullOrEmpty(record.Geo.Place.Address))
                {
                    bp.Attributes.Add(new PostAttribute(PostParams.Tags, record.Geo.Place.Address));//адресс текстом
                }
            }
            bp.Body = bp.Body.Trim();
            return bp;
        }

        /// <summary>
        /// Получение одного поста по ссылке на него и данных о пользователе
        /// </summary>
        /// <param name="linkOnPost">ссылка на пост</param>
        public override BlogPost GetOnePost(string linkOnPost)
        {
            return GetOnePost(linkOnPost,"",BlogType.Unknown);
        }

        /// <summary>
        /// Получение одного поста по ссылке на него и данных о пользователе
        /// </summary>
        /// <param name="linkOnPost">ссылка на пост</param>
        /// <param name="userName">имя пользователя</param>
        /// <param name="btype">тип блогплатформы</param>
        /// <remarks>Может генерить исключения</remarks>
        public override BlogPost GetOnePost(string linkOnPost, string userName, BlogType btype)
        {
            List<BlogPost> bps = GetManyPost(new[] {linkOnPost});
            return bps.Count > 0 ? bps[0] : null;
        }

        /// <summary>
        /// Getting user's PIC
        /// </summary>
        /// <param name="userName">long , integer</param>
        /// <param name="btype"></param>
        /// <returns>URL, string</returns>
        public override string ExtractAvatar(string userName, BlogType btype)
        {
            try
            {
                if (btype == BlogType.Community || userName.StartsWith("-"))
                {
                    //club
                    var client = new VkontakteSharp.Endpoints.Groups.Authenticated(APIKey, ServiceKey);
                    Response<List<Group>> usr = client.GetById(new[] { userName });
                    if (usr != null && usr.Data.Count > 0)
                    {
                        return usr.Data[0].PhotoMedium;
                    }
                }
                else
                {
                    var client = new VkontakteSharp.Endpoints.Users.Authenticated(APIKey, ServiceKey);
                    Response<List<User>> usr = client.GetById(new[] {userName}, ProfileFields.photo_medium);
                    if (usr != null && usr.Data.Count > 0)
                    {
                        return usr.Data[0].PhotoMedium;
                    }
                }
            }catch(Exception e1)
            {
                Debug.Write(e1);
            }
            return null;
        }

        #endregion

        #region ILike
        
        public Dictionary<string, int> GetLikes(Uri Link)
        {
            var oRet = new Dictionary<string, int>();
            try
            {
                HttpWebRequest myHttpWebRequest;
                HttpWebResponse myHttpWebResponse;
                string faceLink = String.Format(
                        "https://vk.com/share.php?act=count&index=1&url={0}",
                        HttpUtility.UrlEncode(Link.OriginalString));
                myHttpWebRequest = MakeRequest(faceLink);
                myHttpWebRequest.Method = "GET"; // метод GET
                myHttpWebRequest.Referer = "http://vk.com";
                //получаем ответ и его разбираем...
                myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
                string result = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding.UTF8).ReadToEnd();
                myHttpWebResponse.Close();
                MatchCollection mc = Regex.Matches(result, @"\b(\d+)\b");
                if (mc.Count > 1)
                {
                    oRet.Add("count", int.Parse(mc[1].Groups[1].Value));
                }
            }
            catch (Exception e1)
            {
                Debug.WriteLine(e1);
            }
            return oRet;
        }

        #endregion


    }
}